package com.dy.yunying.biz.service.manage.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dy.yunying.api.entity.ParentGameDO;
import com.dy.yunying.api.entity.ParentGameDOSums;
import com.dy.yunying.api.enums.PackTypeEnum;
import com.dy.yunying.api.req.GameSupplierReq;
import com.dy.yunying.api.req.ParentGameReq;
import com.dy.yunying.api.vo.ParentGameVO;
import com.dy.yunying.api.vo.WanGameReqVo;
import com.dy.yunying.biz.dao.manage.ext.ParentGameDOMapperExt;
import com.dy.yunying.biz.service.manage.AdRoleGameService;
import com.dy.yunying.biz.service.manage.GameService;
import com.dy.yunying.biz.service.manage.ParentGameService;
import com.dy.yunying.biz.service.manage.WanGameSupplierService;
import com.google.common.collect.Lists;
import com.pig4cloud.pig.common.core.util.EBeanUtil;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.security.util.SecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;

/**
 * @author ：lile
 * @date ：2021/5/29 15:19
 * @description：
 * @modified By：
 */
@Slf4j
@Service
public class ParentGameServiceImpl extends ServiceImpl<ParentGameDOMapperExt, ParentGameDO> implements ParentGameService {

	private static final Map<String, Field> PGAME_FILED_MAP = EBeanUtil.getFieldMap(ParentGameDO.class);

	@Autowired
	private ParentGameDOMapperExt parentGameDOMapperExt;

	@Autowired
	private GameService gameService;

	@Autowired
	private WanGameSupplierService wanGameSupplierService;

	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	/**
	 * 父游戏查询
	 *
	 * @param req
	 * @return
	 */
	@Override
	public IPage<ParentGameVO> getPage(ParentGameReq req) {
		IPage<ParentGameVO> page = parentGameDOMapperExt.page(req);
		if (page.getRecords() != null && page.getRecords().size() > 0) {
			List<ParentGameVO> list = page.getRecords();
			/*获取三个数值*/
			List<ParentGameDOSums> pSums = parentGameDOMapperExt.selectNums();
			/*获取code和name*/
			ParentGameDO codeReq = new ParentGameDO();
			codeReq.setGname(req.getGname());
			List<ParentGameVO> listCodeAndName = parentGameDOMapperExt.selectParentGameListCodeAndName(codeReq);
			if (CollectionUtils.isNotEmpty(pSums)) {
				Map<Long,ParentGameDOSums> pSumMap = pSums.stream().filter(v -> v.getPgid()!=null)
						.collect(Collectors.toMap(ParentGameDOSums::getPgid, Function.identity()));
					list.forEach(parentGameDO -> {
						ParentGameDOSums p = pSumMap.get(parentGameDO.getId());
						if (p != null) {
							parentGameDO.setPlayernum(p.getRegisterSum());
							parentGameDO.setRecharge(p.getRechargeSum());
							parentGameDO.setSgnum(p.getGameSum());
						}
					});
			}
			if (CollectionUtils.isNotEmpty(listCodeAndName)) {
				List<ParentGameVO> versionList = listCodeAndName.stream().filter(v -> v.getId() != null && null != v.getPackType()).collect(Collectors.toList());
				list.forEach(parentGameDO -> {
					List<Map<String, String>> parentGameVersionList = Lists.newArrayList();
					for (ParentGameVO parentGame : versionList){
						if (parentGame.getId().equals(parentGameDO.getId())){
							Map<String, String> map = new HashMap<>();
							map.put("name", parentGame.getName());
							map.put("code", parentGame.getCode());
							map.put("packType", String.valueOf(parentGame.getPackType()));
							map.put("packTypeText", PackTypeEnum.getName(Integer.valueOf(parentGame.getPackType())));
							parentGameVersionList.add(map);
						}
					}
					parentGameDO.setParentGameVersionList(parentGameVersionList);
				});
			}
			list.forEach(parentGameDO -> {
				if (Objects.isNull(parentGameDO.getSgnum())) {
					parentGameDO.setSgnum(0L);
				}
				if (Objects.isNull(parentGameDO.getPlayernum())) {
					parentGameDO.setPlayernum(0L);
				}
				if (Objects.isNull(parentGameDO.getRecharge())) {
					parentGameDO.setRecharge(BigDecimal.ZERO);
				}
			});
		}
		return page;
	}

	/**
	 * 获取可缓存的主游戏列表
	 *
	 * @param req
	 * @return
	 */
	@Override
	public List<ParentGameDO> getCacheablePGameList(ParentGameReq req) throws Exception {
		final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		final String keyPrefix = "com:yy:parentGame:";
		Collection<Long> ids = req.getIds();
		List<ParentGameDO> resultList = new ArrayList<>();

		// 首先从redis中获取主游戏
		Set<Long> notExistsIdSet = new HashSet<>();
		redisTemplate.setHashValueSerializer(new StringRedisSerializer());
		HashOperations<String, String, String> forHash = redisTemplate.opsForHash();
		for (Long id : ids) {
			Map<String, String> beanMap = forHash.entries(keyPrefix + id);
			if (!beanMap.isEmpty()) {
				resultList.add(EBeanUtil.stringMapToObject(beanMap, ParentGameDO.class, PGAME_FILED_MAP, (c, v) -> {
					try {
						return c == Date.class ? sdf.parse(v) : null;
					} catch (ParseException ignore) {
						return null;
					}
				}));
			} else {
				notExistsIdSet.add(id);
			}
		}
		if (notExistsIdSet.isEmpty()) {
			return resultList;
		}
		// 从数据库中查询缓存没有的数据
		LambdaQueryWrapper<ParentGameDO> wrapper = Wrappers.<ParentGameDO>query().lambda().in(ParentGameDO::getId, notExistsIdSet).orderByDesc(ParentGameDO::getId);
		List<ParentGameDO> gameList = getBaseMapper().selectList(wrapper);
		resultList.addAll(gameList);
		// 将从数据库查询出的主游戏信息写入redis
		redisTemplate.execute(new SessionCallback<Object>() {
			@SneakyThrows
			@Override
			public Object execute(RedisOperations operations) throws DataAccessException {
				operations.multi();
				for (ParentGameDO game : gameList) {
					String key = keyPrefix + game.getId();
					Map<String, String> beanMap = EBeanUtil.objectToStringMap(game, v -> v instanceof Date ? sdf.format(v) : null);
					operations.opsForHash().putAll(key, beanMap);
					operations.expire(key, 5 * 60, TimeUnit.SECONDS);
				}
				operations.exec();
				return null;
			}
		});
		return resultList;
	}


	private ParentGameDO mapToPGame(Map<String, String> beanMap, SimpleDateFormat sdf) throws Exception {
		ParentGameDO game = new ParentGameDO();
		for (Map.Entry<String, String> bean : beanMap.entrySet()) {
			Field field = PGAME_FILED_MAP.get(bean.getKey());
			field.setAccessible(true);
			if (field.getType() == String.class) {
				field.set(game, bean.getValue());
			} else if (field.getType() == Date.class) {
				field.set(game, sdf.parse(bean.getValue()));
			} else if (field.getType() == BigDecimal.class) {
				field.set(game, new BigDecimal(bean.getValue()));
			} else if (field.getType() == Long.class) {
				field.set(game, Long.valueOf(bean.getValue()));
			} else if (field.getType() == Integer.class) {
				field.set(game, Integer.valueOf(bean.getValue()));
			}
			field.setAccessible(false);
		}
		return game;
	}

	private Map<String, String> pGameToMap(ParentGameDO pGame, SimpleDateFormat sdf) throws Exception {
		Map<String, String> result = new HashMap<>();
		for (Map.Entry<String, Field> bean : PGAME_FILED_MAP.entrySet()) {
			Field filed = bean.getValue();
			filed.setAccessible(true);
			Object value = filed.get(pGame);
			if (null == value || (value instanceof String && StringUtils.isEmpty((String) value))) {
				continue;
			}
			if (filed.getType() == String.class) {
				result.put(filed.getName(), (String) value);
			} else if (filed.getType() == Date.class) {
				result.put(filed.getName(), sdf.format((Date) value));
			} else {
				result.put(filed.getName(), value.toString());
			}
			filed.setAccessible(false);
		}
		return result;
	}

	/**
	 * @param parentGame
	 * @return
	 */
	@Override
	public R saveParentGame(ParentGameDO parentGame) {
		ParentGameDO parentGameDB = parentGameDOMapperExt.selectByParentGName(parentGame);
		//核实要添加的父游戏是否存在于数据库中
		int count;
		if (Objects.isNull(parentGameDB)) {
			parentGame.setCreator(SecurityUtils.getUser().getUsername());
			parentGame.setCreateTime(new Date());
			count = baseMapper.insertSelective(parentGame);
			if (count > 0) {
				// 自动创建联调子游戏
				WanGameReqVo wanGameReq = createSonGame(parentGame);
				if (wanGameReq.getId() != null) {
					ParentGameDO parentGameTmp = new ParentGameDO();
					parentGameTmp.setId(parentGame.getId());
					parentGameTmp.setJointGameId(wanGameReq.getId().longValue());
					parentGameDOMapperExt.updateByPrimaryKeySelective(parentGameTmp);
				}
				return R.ok("添加父游戏成功");
			} else {
				return R.failed(0, "添加父游戏失败");
			}
		} else {
			return R.failed(0, "父游戏已存在");
		}
	}


	private WanGameReqVo createSonGame(ParentGameDO parentGame) {
		WanGameReqVo wanGameReq = new WanGameReqVo();
		wanGameReq.setPgid(BigInteger.valueOf(parentGame.getId()));
		wanGameReq.setGname(parentGame.getGname() + "(联调)");
		wanGameReq.setDisplay(1);
		wanGameReq.setPk_name(parentGame.getPkName());
		wanGameReq.setStatus(30);
		wanGameReq.setIshot(0);
		wanGameReq.setIsrecommend(0);
		wanGameReq.setTerminaltype(2);
		wanGameReq.setDisplay(1); // 不展示
		wanGameReq.setIsrecharge("1");
		wanGameReq.setIsdelete(0);
		wanGameReq.setExchange_url(parentGame.getExchangeUrl());
		BigInteger gameId = gameService.insertWanGame(wanGameReq);
		wanGameReq.setId(gameId);
		return wanGameReq;
	}

	/**
	 * 修改父游戏
	 *
	 * @param parentGame
	 * @return
	 */
	@Override
	public R updateParentGame(ParentGameDO parentGame) {
		ParentGameDO parentGameDO = parentGameDOMapperExt.selectByParentGName(parentGame);
		if (Objects.nonNull(parentGameDO) && !parentGameDO.getId().equals(parentGame.getId())) {
			return R.failed(1, "修改的父游戏已存在");
		} else {
			int count;
			count = parentGameDOMapperExt.updateByPrimaryKeySelective(parentGame);
			if (count > 0) {
				//批量更新子游戏回调地址
				GameSupplierReq req = new GameSupplierReq();
				req.setPgid(Integer.parseInt(parentGame.getId().toString()));
				// 兑换链接
				String exchangeUrl = StringUtils.isBlank(parentGame.getExchangeUrl()) ? null : parentGame.getExchangeUrl();
				if(StringUtils.isNotBlank(exchangeUrl)){
					req.setExchangeUrl(exchangeUrl);
					wanGameSupplierService.updateExchangeUrl(req);
				}
				return R.ok(1, "修改的父游戏成功");
			}
			return R.failed(0, "修改的父游戏失败");
		}
	}

	@Override
	public ParentGameDO getByPK(Long gameId) {
		ParentGameDO parentGameDO = null;
		try {
			if (gameId == null || gameId <= 0) {
				return null;
			}
			HashOperations<String, String, String> forHash = redisTemplate.opsForHash();
			final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			final String keyPrefix = "com:yy:parentGame:";
			Map<String, String> beanMap = forHash.entries(keyPrefix + gameId);
			if (!beanMap.isEmpty()) {
				parentGameDO = mapToPGame(beanMap, sdf);
				return parentGameDO;
			}
			parentGameDO = parentGameDOMapperExt.selectByPrimaryKey(gameId);
			if (parentGameDO != null) {
				// 将从数据库查询出的主游戏信息写入redis
				ParentGameDO finalParentGameDO = parentGameDO;
				redisTemplate.execute(new SessionCallback<Object>() {
					@SneakyThrows
					@Override
					public Object execute(RedisOperations operations) throws DataAccessException {
						operations.multi();

						String key = keyPrefix + finalParentGameDO.getId();
						Map<String, String> beanMap = pGameToMap(finalParentGameDO, sdf);
						operations.opsForHash().putAll(key, beanMap);
						operations.expire(key, 5 * 60, TimeUnit.SECONDS);

						operations.exec();
						return null;
					}
				});
			}
		} catch (Exception e) {
			log.error("查询游戏信息失败，id:[{}]", gameId);
			log.error("查询游戏信息失败", e);
		}
		return parentGameDO;
	}

	@Override
	public List<ParentGameVO> versionVOList(ParentGameDO req) {
		return parentGameDOMapperExt.versionVOList(req);
	}
}
